home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 1 / QRZ Ham Radio Callsign Database - December 1993.iso / ucsd / packet / tcpip / amiga / asrc29k.lha / bmutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-08  |  26.8 KB  |  1,019 lines

  1. /*
  2.  *   Simple mail user interface for KA9Q IP/TCP package.
  3.  *   A.D. Barksdale Garbee II, aka Bdale, N3EUA
  4.  *   Copyright 1986 Bdale Garbee, All Rights Reserved.
  5.  *   Permission granted for non-commercial copying and use, provided
  6.  *   this notice is retained.
  7.  *   Copyright 1987 1988 Dave Trulli NN2Z, All Rights Reserved.
  8.  *   Permission granted for non-commercial copying and use, provided
  9.  *   this notice is retained.
  10.  *
  11.  *   Ported to NOS at 900120 by Anders Klemets SM0RGV.
  12.  */
  13.  
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <ctype.h>
  17. #include <time.h>
  18. #include "global.h"
  19. #include "ftpserv.h"
  20. #include "smtp.h"
  21. #include "proc.h"
  22. #include "usock.h"
  23. #include "telnet.h"
  24. #include "timer.h"
  25. #include "files.h"
  26.  
  27. #define      SETVBUF
  28. #if   defined(UNIX) || defined(MICROSOFT)
  29. #include   <sys/types.h>
  30. #endif
  31.  
  32. #include <fcntl.h>
  33. #include "bm.h"
  34. #include "mailbox.h"
  35.  
  36. #ifdef SETVBUF
  37. #define      MYBUF   1024
  38. char   *stdinbuf = NULLCHAR;   /* the stdio buffer for the mail file */
  39. char   *stdoutbuf = NULLCHAR;   /* the stdio file io buffer for the temp file */
  40. #endif
  41.  
  42. extern long ftell();
  43. char Badmsg[] = "Invalid Message number %d\n";
  44. char Nomail[] = "No messages\n";
  45. static char Noaccess[] = "Unable to access %s\n";
  46. static int readnotes __ARGS((struct mbx *m,FILE *ifile,int update));
  47. static long isnewmail __ARGS((struct mbx *m));
  48. static int initnotes __ARGS((struct mbx *m));
  49. static int lockit __ARGS((struct mbx *m));
  50. static long fsize __ARGS((char *name));
  51. static void mfclose __ARGS((struct mbx *m));
  52.  
  53. static int initnotes(m)
  54. struct mbx *m;
  55. {
  56.    FILE *tmpfile();
  57.    FILE *ifile;
  58.    register struct let *cmsg;
  59.    char buf[256];
  60.    int i, ret;
  61.  
  62.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  63.    if ((ifile = fopen(buf,READ_TEXT)) == NULLFILE)
  64.       return 0;
  65.    fseek(ifile,0L,2);    /* go to end of file */
  66.    m->mboxsize = ftell(ifile);
  67.    rewind(ifile);
  68.    if(!stricmp(m->area,m->name)) /* our private mail area */
  69.       m->mysize = m->mboxsize;
  70.    if ((m->mfile = tmpfile()) == NULLFILE) {
  71.       (void) fclose(ifile);
  72.       return -1;
  73.    }
  74. #ifdef   SETVBUF
  75.    if (stdinbuf == NULLCHAR)
  76.       stdinbuf = mallocw(MYBUF);
  77.    setvbuf(ifile, stdinbuf, _IOFBF, MYBUF);
  78.    if (stdoutbuf == NULLCHAR)
  79.       stdoutbuf = mallocw(MYBUF);
  80.    setvbuf(m->mfile, stdoutbuf, _IOFBF, MYBUF);
  81. #endif
  82.    m->nmsgs = 0;
  83.    m->current = 0;
  84.    m->change = 0;
  85.    m->newmsgs = 0;
  86.    m->anyread = 0;
  87.    /* Allocate space for reading messages */
  88.    free((char *)m->mbox);
  89.    m->mbox = (struct let *)callocw(Maxlet+1,sizeof(struct let));
  90.    ret = readnotes(m,ifile,0);
  91.    (void) fclose(ifile);
  92. #ifdef SETVBUF
  93.    free(stdinbuf);
  94.    stdinbuf = NULLCHAR;
  95. #endif
  96.    if (ret != 0)
  97.       return -1;
  98.    for (cmsg = &m->mbox[1],i = 1; i <= m->nmsgs; i++, cmsg++)  
  99.       if ((cmsg->status & BM_READ) == 0) {
  100.          m->newmsgs++;
  101.          if (m->current == 0)
  102.             m->current = i;
  103.       }
  104.    /* start at one if no new messages */
  105.    if (m->current == 0)
  106.       m->current++;
  107.    return 0;
  108. }
  109.  
  110. /* readnotes assumes that ifile is pointing to the first
  111.  * message that needs to be read.  For initial reads of a
  112.  * notesfile, this will be the beginning of the file.  For
  113.  * rereads when new mail arrives, it will be the first new
  114.  * message.  */
  115. static int readnotes(m,ifile,update)
  116. struct mbx *m;
  117. FILE *ifile ;
  118. int update;   /* true if this is not the initial read of the notesfile */
  119. {
  120.    char tstring[LINELEN];
  121.    long cpos;
  122.    register struct   let *cmsg;
  123.    register char *line;
  124.  
  125.    cmsg = (struct let *)NULL;
  126.    line = tstring;
  127.    while(fgets(line,LINELEN,ifile) != NULLCHAR) {
  128.       /* scan for begining of a message */
  129.       if(strncmp(line,"From ",5) == 0) {
  130.          pwait(NULL);
  131.          cpos = ftell(m->mfile);
  132.          fputs(line,m->mfile);
  133.          if (m->nmsgs == Maxlet) {
  134.             tprintf("Mail box full: > %d messages\n",Maxlet);
  135.             mfclose(m);
  136.             return -1;
  137.          }
  138.          m->nmsgs++;
  139.          cmsg = &m->mbox[m->nmsgs];
  140.          cmsg->start = cpos;
  141.          if(!update)
  142.             cmsg->status = 0;
  143.          cmsg->size = strlen(line);
  144.          while (fgets(line,LINELEN,ifile) != NULLCHAR) {
  145.             if (*line == '\n') { /* done header part */
  146.                cmsg->size++;
  147.                putc(*line, m->mfile);
  148.                break;
  149.             }
  150.             if (htype(line) == STATUS) {
  151.                if (line[8] == 'R') 
  152.                   cmsg->status |= BM_READ;
  153.                continue;
  154.             }
  155.             cmsg->size += strlen(line);
  156.             if (fputs(line,m->mfile) == EOF) {
  157.                tprintf("tmp file: %s",sys_errlist[errno]);
  158.                mfclose(m);
  159.                return -1;
  160.             }
  161.          }
  162.       } else if (cmsg) {
  163.          cmsg->size += strlen(line);
  164.          fputs(line,m->mfile);
  165.       }
  166.    }
  167.    return 0;
  168. }
  169.  
  170. /* list headers of a notesfile a message */
  171. int dolistnotes(argc,argv,p)
  172. int argc;
  173. char *argv[];
  174. void *p;
  175. {
  176.    struct mbx *m;
  177.    register struct let *cmsg;
  178.    register char *cp, *s;
  179.    char smtp_date[SLINELEN], smtp_from[SLINELEN], smtp_to[SLINELEN];
  180.    char smtp_subject[SLINELEN], tstring[LINELEN], type;
  181.    int start, stop;
  182.    long size;
  183.  
  184.    m = (struct mbx *) p;
  185.    if (m->mfile == NULLFILE) {
  186.       tprintf(Nomail);
  187.       return 0;
  188.    }
  189.  
  190.    if((m->stype == '>' || m->stype == '<') && argc == 1) {
  191.       tprintf("Search criterium needed !\n");
  192.       return 0;
  193.    }
  194.  
  195.    tprintf("Mail area: %s  %d message%s -  %d new\n\n"
  196.            "St.  #       TO     FROM    DATE  TIME   SIZE SUBJECT\n",
  197.            m->area, m->nmsgs, m->nmsgs == 1 ? " " : "s ", m->newmsgs);
  198.    stop = m->nmsgs;
  199.    if(m->stype == 'L') {      /* LL (List Latest) command */
  200.       if(argc > 1)
  201.          start = stop - atoi(argv[1]) + 1;
  202.       else
  203.          start = stop;
  204.    } else {
  205.       if((m->stype == '>' || m->stype == '<')) {
  206.          start = 1;
  207.          stop = m->nmsgs;
  208.       } else {
  209.          if(argc > 1)
  210.             start = atoi(argv[1]);
  211.          else
  212.             start = 1;
  213.          if(argc > 2)
  214.             stop = atoi(argv[2]);
  215.       }
  216.    }
  217.    if(stop > m->nmsgs)
  218.       stop = m->nmsgs;
  219.    if(start < 1 || start > stop) {
  220.       tprintf("Invalid range.\n");
  221.       return 0;
  222.    }
  223.    for (cmsg = &m->mbox[start]; start <= stop; start++, cmsg++) {
  224.       *smtp_date = '\0';
  225.       *smtp_from = '\0';
  226.       *smtp_subject = '\0';
  227.       *smtp_to = '\0';
  228.       type = ' ';
  229.       fseek(m->mfile,cmsg->start,0);
  230.       size = cmsg->size;
  231.       while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  232.          != NULLCHAR) {
  233.          if (*tstring == '\n')   /* end of header */
  234.             break;
  235.          size -= strlen(tstring);
  236.          rip(tstring);
  237.          /* handle continuation later */
  238.          if (*tstring == ' '|| *tstring == '\t')
  239.             continue;
  240.          switch(htype(tstring)) {
  241.          case FROM:
  242.             cp = getaddress(tstring,0);
  243.             sprintf(smtp_from,"%s",
  244.                cp != NULLCHAR ? cp : "");
  245.             if((cp=strchr(smtp_from,'@')) != NULLCHAR)
  246.                *cp = '\0';
  247.             if((cp=strchr(smtp_from,'%')) != NULLCHAR)
  248.                *cp = '\0';
  249.             break;
  250.          case SUBJECT:
  251.             sprintf(smtp_subject,"%s",&tstring[9]);
  252.             break;
  253.          case DATE:
  254.             if ((cp = strchr(tstring,',')) == NULLCHAR)
  255.                cp = &tstring[6];
  256.             else
  257.                cp++;
  258.             /* skip spaces */
  259.             while (*cp == ' ') cp++;
  260.             if(strlen(cp) < 17)
  261.                break;    /* not a valid length */
  262.             s = smtp_date;
  263.             /* copy day */
  264.             if (atoi(cp) < 10 && *cp != '0') {
  265.                *s++ = ' ';
  266.             } else
  267.                *s++ = *cp++;
  268.             *s++ = *cp++;
  269.             *s++ = ' ';
  270.             *s = '\0';
  271.             while (*cp == ' ')
  272.                cp++;
  273.             strncat(s,cp,3);   /* copy month */
  274.             cp += 3;
  275.             while (*cp == ' ')
  276.                cp++;
  277.             /* skip year */
  278.             while (isdigit(*cp))
  279.                cp++;
  280.             /* copy time */
  281.             strncat(s,cp,6); /* space hour : min */
  282.             break;
  283.          case BBSTYPE:
  284.             type = tstring[16];
  285.             break;
  286.          case TO:
  287.             sprintf(smtp_to,"%s",&tstring[4]);
  288.             if((cp=strchr(smtp_to,'@')) != NULLCHAR)
  289.                *cp = '\0';
  290.             break;
  291.          case NOHEADER:
  292.             break;
  293.          }
  294.       }
  295.       if(m->stype == ' ' || m->stype == 'L' ||
  296.          (type == m->stype && m->stype != ' ') ||
  297.          ((m->stype == '<') && 
  298.           (strstr(strlwr(smtp_from),argv[1]) != NULLCHAR)) ||
  299.          ((m->stype == '>') && 
  300.           (strstr(strlwr(smtp_to),argv[1]) != NULLCHAR))) 
  301.         tprintf("%c%c%c%3d %8.8s %8.8s  %-13.13s %5ld %.35s\n",
  302.             (start == m->current ? '>' : ' '),
  303.             (cmsg->status & BM_DELETE ? 'D' : ' '),
  304.             (cmsg->status & BM_READ ? 'Y' : 'N'),
  305.             start, smtp_to, smtp_from, smtp_date,
  306.             cmsg->size, smtp_subject);
  307.    }
  308.    return 0;
  309. }
  310.  
  311. /*  save msg on stream - if noheader set don't output the header */
  312. int msgtofile(m,msg,tfile,noheader)
  313. struct mbx *m;
  314. int msg;
  315. FILE *tfile;   /* already open for write */
  316. int noheader;
  317. {
  318.    char tstring[LINELEN];
  319.    long size;
  320.  
  321.    if (m->mfile == NULLFILE) {
  322.       tprintf(Nomail);
  323.       return -1;
  324.    }
  325.    fseek(m->mfile,m->mbox[msg].start,0);
  326.    size = m->mbox[msg].size;
  327.  
  328.    if (noheader) {
  329.       /* skip header */
  330.       while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  331.          != NULLCHAR) {
  332.          size -= strlen(tstring);
  333.          if (*tstring == '\n')
  334.             break;
  335.       }
  336.    }
  337.    while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  338.       != NULLCHAR) {
  339.       size -= strlen(tstring);
  340.       fputs(tstring,tfile);
  341.       if (ferror(tfile)) {
  342.          tprintf("Error writing mail file\n");
  343.          return -1;
  344.       }
  345.    }
  346.    return 0;
  347. }
  348.  
  349. /*  dodelmsg - delete message in current notesfile */
  350. int dodelmsg(argc,argv,p)
  351. int argc;
  352. char *argv[];
  353. void *p;
  354. {
  355.    struct mbx *m;
  356.    int msg,i;
  357.    m = (struct mbx *) p;
  358.    if (m->mfile == NULLFILE) {
  359.       tprintf(Nomail);
  360.       return 0;
  361.    }
  362.    for(i = 1; i < argc; ++i) {
  363.       msg = atoi(argv[i]);
  364.       if(msg < 0 || msg > m->nmsgs) {
  365.          tprintf(Badmsg,msg);
  366.          continue;
  367.       }
  368.       /* Check if we have permission to delete others mail */
  369.       if(!(m->privs & FTP_WRITE) && stricmp(m->area,m->name)) {
  370.          tprintf(Noperm);
  371.          return 0;
  372.       }
  373.       /* A second kill makes the message unkilled -- SM6RPZ */
  374.       if(m->mbox[msg].status & BM_DELETE) {
  375.               /* Message is killed - unkill it! */
  376.               m->mbox[msg].status &= ~BM_DELETE;
  377.               tprintf("Msg %d Unkilled.\n",msg);
  378.       } else {
  379.               /* Kill message */
  380.               m->mbox[msg].status |= BM_DELETE;
  381.               tprintf("Msg %d Killed.\n", msg);
  382.       }
  383.       m->change = 1;
  384.    }
  385.    return 0;
  386. }
  387.  
  388. /* close the temp file while coping mail back to the mailbox */
  389. int closenotes(m)
  390. struct mbx *m;
  391. {
  392.    register struct   let *cmsg;
  393.    register char *line;
  394.    char tstring[LINELEN], buf[256];
  395.    long size;
  396.    int i, nostatus, nodelete;
  397.    FILE *nfile;
  398.  
  399.    if (m->mfile == NULLFILE)
  400.       return 0;
  401.  
  402.    if(!m->change) {      /* no changes were made */
  403.       mfclose(m);
  404.       m->mboxsize = 0;
  405.       return 0;
  406.    }
  407.    /* If this area is a public message area, then we will not add a
  408.     * Status line to indicate that the message has been read.
  409.     */
  410.    nostatus = isarea(m->area);
  411.  
  412.    /* Don't delete messages from public message areas unless you are
  413.     * a BBS.
  414.     */
  415.    if(nostatus)
  416.       nodelete = !(m->privs & SYSOP_CMD);
  417.    else
  418.       nodelete = 0;
  419.  
  420.    /* See if any messages have been forwarded, otherwise just close
  421.     * the file and return since there is nothing to write back.
  422.     */
  423.    if(nostatus && nodelete) {
  424.       for(i=1; i <= m->nmsgs; ++i)
  425.          if(m->mbox[i].status & BM_FORWARDED)
  426.             break;
  427.       if(i > m->nmsgs) {
  428.          mfclose(m);
  429.          m->mboxsize = 0;
  430.          return 0;
  431.       }
  432.    }
  433.    line = tstring;
  434.    scanmail(m);
  435.    if(lockit(m))
  436.       return -1;
  437.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  438.    if ((nfile = fopen(buf,WRITE_TEXT)) == NULLFILE) {
  439.       tprintf(Noaccess,buf);
  440.       mfclose(m);
  441.       m->mboxsize = 0;
  442.       rmlock(Mailspool,m->area);
  443.       return -1;
  444.    }
  445.    /* copy tmp file back to notes file */
  446.    for (cmsg = &m->mbox[1],i = 1; i <= m->nmsgs; i++, cmsg++) {
  447.       fseek(m->mfile,cmsg->start,0);
  448.       size = cmsg->size;
  449.       /* It is not possible to delete messages if nodelete is set */
  450.       if ((cmsg->status & BM_DELETE) && !nodelete)
  451.          continue;
  452.       /* copy the header */
  453.       while (size > 0 && fgets(line,LINELEN,m->mfile) != NULLCHAR) {
  454.          size -= strlen(line);
  455.          if (*line == '\n') {
  456.             if (cmsg->status & BM_FORWARDED)
  457.                fprintf(nfile,"%s%s\n",Hdrs[XFORWARD],
  458.                   m->name);
  459.             if ((cmsg->status & BM_READ) != 0 && !nostatus)
  460.                fprintf(nfile,"%sR\n",Hdrs[STATUS]);
  461.             fprintf(nfile,"\n");
  462.             break;
  463.          }
  464.          fputs(line,nfile);
  465.          /* pwait(NULL);  can cause problems if exiting NOS */
  466.       }
  467.       while (size > 0 && fgets(line,LINELEN,m->mfile) != NULLCHAR) {
  468.          fputs(line,nfile);
  469.          size -= strlen(line);
  470.          /* pwait(NULL);   dont want no damaged files */
  471.          if (ferror(nfile)) {
  472.             tprintf("Error writing mail file\n");
  473.             (void) fclose(nfile);
  474.             mfclose(m);
  475.             m->mboxsize = 0;
  476.             rmlock(Mailspool,m->area);
  477.             return -1;
  478.          }
  479.       }
  480.    }
  481.    m->nmsgs = 0;
  482.    if (!stricmp(m->name,m->area))
  483.       m->mysize = ftell(nfile); /* Update the size of our mailbox */
  484.    /* remove a zero length file */
  485.    if (ftell(nfile) == 0L)
  486.       (void) unlink(buf);
  487.    (void) fclose(nfile);
  488.    mfclose(m);
  489.    m->mboxsize = 0;
  490.    rmlock(Mailspool,m->area);
  491.    pwait(NULL);
  492.    return 0;
  493. }
  494.  
  495. /* Returns 1 if name is a public message Area, 0 otherwise */
  496. int isarea(name)
  497. char *name;
  498. {
  499.    char buf[LINELEN], *cp;
  500.    FILE *fp;
  501.    if((fp = fopen(Arealist,READ_TEXT)) == NULLFILE)
  502.       return 0;
  503.    while(fgets(buf,sizeof(buf),fp) != NULLCHAR) {
  504.       /* The first word on each line is all that matters */
  505.       if((cp = strchr(buf,' ')) == NULLCHAR)
  506.          if((cp = strchr(buf,'\t')) == NULLCHAR)
  507.             continue;
  508.       *cp = '\0';
  509.       if((cp = strchr(buf,'\t')) != NULLCHAR)
  510.          *cp = '\0';
  511.       if(stricmp(name,buf) == 0) {   /* found it */
  512.          fclose(fp);
  513.          return 1;
  514.       }
  515.    }
  516.    fclose(fp);
  517.    return 0;
  518. }
  519.  
  520. static int lockit(m)
  521. struct mbx *m;
  522. {
  523.    int c, cnt = 0;
  524.  
  525.    while(mlock(Mailspool,m->area)) {
  526.       pause(1000/MSPTICK);   /* Wait one second */
  527.       if(++cnt == 10) {
  528.          cnt = 0;
  529.          c = keywait("Mail file is busy, Abort or Retry ? ",0);
  530.          if (c == 'A' || c == 'a' || c == EOF) {
  531.             mfclose(m);
  532.             return 1;
  533.          }
  534.       }
  535.    }
  536.    return 0;
  537. }
  538.  
  539. /* read the next message or the current one if new */
  540. int doreadnext(argc,argv,p)
  541. int argc;
  542. char *argv[];
  543. void *p;
  544. {
  545.    struct mbx *m;
  546.    char buf[10], *newargv[2];
  547.    m = (struct mbx *) p;
  548.    if (m->mfile == NULLFILE)
  549.       return 0;
  550.    if ((m->mbox[m->current].status & BM_READ) != 0) {
  551.       if (m->current == 1 && m->anyread == 0)
  552.          ;
  553.       else if (m->current < m->nmsgs) {
  554.          m->current++;
  555.       } else {
  556.          tprintf("Last message\n");
  557.          return 0;
  558.       }
  559.    }
  560.    sprintf(buf,"%d",m->current);
  561.    newargv[0] = "read";
  562.    newargv[1] = buf;
  563.    m->anyread = 1;
  564.    return doreadmsg(2,newargv,p);
  565. }
  566.  
  567. /*  display message on the crt given msg number */
  568. int doreadmsg(argc,argv,p)
  569. int argc;
  570. char *argv[];
  571. void *p;
  572. {
  573.    struct mbx *m;
  574.    register int c, col, lin;
  575.    char buf[MAXCOL+2], *cp, *cp2;
  576.    int msg, cnt, i, tcpcon, verbose = 0, mbxheader = 0, pathcol;
  577.    int header, lastheader;
  578.    long size;
  579.    struct usock *up;
  580.  
  581.    m = (struct mbx *) p;
  582.    if (m->mfile == NULLFILE) {
  583.       tprintf(Nomail);
  584.       return 0;
  585.    }
  586.    if(*argv[0] == 'v')
  587.       verbose = 1;      /* display all header lines */
  588.    if((up = itop(Curproc->output)) != NULLUSOCK && up->type == TYPE_TCP)
  589.       tcpcon = 1;   /* This is a TCP (i.e. Telnet) connection */
  590.    else
  591.       tcpcon = 0;
  592.    lin = MAXLIN-1;
  593.    for(i = 1; i < argc; ++i) {
  594.       msg = atoi(argv[i]);
  595.       if( msg < 1 || msg > m->nmsgs) {
  596.          tprintf(Badmsg,msg);
  597.          return 0;
  598.       }
  599.       fseek(m->mfile,m->mbox[msg].start,0);
  600.       size = m->mbox[msg].size;
  601.       m->current = msg;
  602.       header = NOHEADER;
  603.  
  604.       tprintf("Message #%d %s\n", msg,
  605.          m->mbox[msg].status & BM_DELETE ? "[Deleted]" : "");
  606.       if ((m->mbox[msg].status & BM_READ) == 0) {
  607.          m->mbox[msg].status |= BM_READ;
  608.          m->change = 1;
  609.          m->newmsgs--;
  610.       }
  611.       --lin;
  612.       while (!feof(m->mfile) && size > 0) {
  613.          for (col = 0;  col < MAXCOL;) {
  614.             c = getc(m->mfile);
  615.             size--;
  616.             if (feof(m->mfile) || size == 0) /* end this line */
  617.                break;
  618.             if (c == '\t') {
  619.                cnt = col + 8 - (col & 7);
  620.                if (cnt >= MAXCOL) /* end this line */
  621.                   break;
  622.                while (col < cnt)
  623.                   buf[col++] = ' ';
  624.             } else {
  625.                if (c == '\n')
  626.                   break;
  627.                buf[col++] = c;
  628.             }
  629.          }
  630.          if(col < MAXCOL)
  631.             buf[col++] = '\n';
  632.          buf[col] = '\0';
  633.          if(mbxheader > 0) {
  634.             /* Digest R: lines and display as a Path: line */
  635.             if(strncmp(buf,"R:",2) != 0 ||
  636.                (cp = strchr(buf,'@')) == NULLCHAR) {
  637.                tputc('\n');
  638.                mbxheader = -1; /* don't get here again */
  639.                verbose = 1;
  640.             } else {
  641.                if(*(++cp) == ':')
  642.                   ++cp;
  643.                for(cp2 = cp; isalnum(*cp2); ++cp2)  ;
  644.                *cp2 = '\0';
  645.                if(mbxheader++ == 1) {
  646.                   tputs("Path: ");
  647.                   pathcol = 5;
  648.                   --lin;
  649.                } else {
  650.                   tputc('!');
  651.                   if(++pathcol + strlen(cp) > MAXCOL-2) {
  652.                      tputs("\n      ");
  653.                      pathcol = 5;
  654.                      --lin;
  655.                   }
  656.                }
  657.                tputs(cp);
  658.                pathcol += strlen(cp);
  659.             }
  660.          }
  661.          if(col == 1 && !verbose && !mbxheader)
  662.             /* last header line reached */
  663.             mbxheader = 1;
  664.          if(verbose)
  665.             tputs(buf);
  666.          else {
  667.             lastheader = header;
  668.             if(!isspace(*buf))
  669.                header = htype(buf);
  670.             else
  671.                header = lastheader;
  672.             switch(header) {
  673.             case TO:
  674.             case CC:
  675.             case FROM:
  676.             case DATE:
  677.             case SUBJECT:
  678.                tputs(buf);
  679.                break;
  680.             default:
  681.                ++lin;
  682.             }
  683.          }
  684.          if(tcpcon && --lin == 0){
  685.             tprintf("--More--%c%c%c",IAC,WILL,TN_ECHO);
  686.             usflush(Curproc->output);
  687.             /* discard the response */
  688.             while((c = recvchar(Curproc->input)) == IAC){
  689.                c = recvchar(Curproc->input);
  690.                if(c > 250 && c < 255)
  691.                   recvchar(Curproc->input);
  692.             }
  693.             tprintf("\r           \r%c%c%c",IAC,WONT,TN_ECHO);
  694.             usflush(Curproc->output);
  695.             lin = MAXLIN-1;
  696.             if(c == -1 || c == 'q' || c == 'Q')
  697.                break;
  698.             if(c == '\n' || c == '\r')
  699.                lin = 1;
  700.          }
  701.       }
  702.    }
  703.    return 0;
  704. }
  705.  
  706. /* Set up m->to when replying to a message.
  707.    The subject is returned in m->line.  */
  708. int mbx_reply(argc,argv,m,cclist,rhdr)
  709. int argc;
  710. char *argv[];
  711. struct mbx *m;
  712. struct list **cclist;   /* Pointer to buffer for pointers to cc recipients */
  713. char **rhdr;      /* Pointer to buffer for extra reply headers */
  714. {
  715.    char subject[MBXLINE], *msgid = NULLCHAR, *date = NULLCHAR;
  716.    char *cp, *cp2, *nic, newto[80];
  717.    int msg, lastheader, header = NOHEADER, NTSreply;
  718.    long size;
  719.  
  720.    /* Free anything that might be allocated
  721.     * since the last call to mbx_to() or mbx_reply()
  722.     */
  723.    free(m->to);
  724.    m->to = NULLCHAR;
  725.    free(m->tofrom);
  726.    m->tofrom = NULLCHAR;
  727.    free(m->tomsgid);
  728.    m->tomsgid = NULLCHAR;
  729.    free(m->origto);
  730.    m->origto = NULLCHAR;
  731.    subject[0] = '\0';
  732.  
  733.    if(argc == 1)
  734.       msg = m->current;
  735.    else
  736.       msg = atoi(argv[1]);
  737.    if (m->mfile == NULLFILE) {
  738.       if(m->sid & MBX_SID)
  739.          tputs("NO - ");
  740.       tputs(Nomail);
  741.       return 0;
  742.    }
  743.    if(msg < 1 || msg > m->nmsgs) {
  744.       if(m->sid & MBX_SID)
  745.          tputs("NO - ");
  746.       tputs(Badmsg);
  747.       return -1;
  748.    }
  749.    fseek(m->mfile,m->mbox[msg].start,0);
  750.    size = m->mbox[msg].size;
  751.    m->current = msg;
  752.    NTSreply = 0;
  753.    while(size > 0 && fgets(m->line,MBXLINE-1,m->mfile) != NULLCHAR) {
  754.       size -= strlen(m->line);
  755. #ifdef xxx
  756.       if(m->line[0] == '\n')   /* end of header */
  757.          break;
  758. #endif
  759.       rip(m->line);
  760.       lastheader = header;
  761.       if(!isspace(m->line[0])) {
  762.          header = htype(m->line);
  763.          lastheader = NOHEADER;
  764.       }
  765.       switch(header) {
  766.       case SUBJECT:
  767.          if(strlen(m->line) > 11 && !strnicmp(&m->line[9],"Re:",3))
  768.             strcpy(subject,&m->line[9]);
  769.          else
  770.             sprintf(subject,"Re: %s",&m->line[9]);
  771.          break;
  772.       case FROM:
  773.          if(m->to == NULLCHAR && (cp = getaddress(m->line,0)) !=
  774.             NULLCHAR)
  775.             m->to = strdup(cp);
  776.          if(strstr(m->to,".bbs")) 
  777.             NTSreply = 1;
  778.          break;
  779.       case RCOLON:
  780.          nic = strstr(strdup(m->line),"@");
  781.          if((cp2=strchr(nic,' ')) != NULLCHAR)
  782.             *cp2 = '\0';
  783.          break;
  784.       case REPLYTO:
  785.          if((cp = getaddress(m->line,0)) != NULLCHAR) {
  786.             free(m->to);
  787.             m->to = strdup(cp);
  788.          }
  789.          break;
  790.       case MSGID:
  791.          free(msgid);
  792.          msgid = strdup(&m->line[12]);
  793.          break;
  794.       case DATE:
  795.          free(date);
  796.          date = strdup(&m->line[6]);
  797.          break;
  798.       case TO:
  799.       case CC:
  800.          /* Put addresses on To and Cc lines in cclist */
  801.          cp = m->line;
  802.          m->line[strlen(cp)+1] = '\0';   /* add extra null at end */
  803.          for(;;) {
  804.             if((cp = getaddress(cp,lastheader == header ||
  805.                cp != m->line)) == NULLCHAR)
  806.                break;
  807.             addlist(cclist,cp,0);
  808.             /* skip to next address, if any */
  809.             cp += strlen(cp) + 1;
  810.          }
  811.          break;
  812.       }
  813.    }
  814.  
  815.    if(NTSreply) {
  816.       if((cp2=strchr(m->to,'%')) != NULLCHAR)
  817.             *cp2 = '\0';
  818.       if((cp2=strchr(nic,':')) != NULLCHAR)
  819.             cp2 = &nic[2];
  820.       else
  821.             cp2 = &nic[1];
  822.       sprintf(newto, "%s@%s", m->to, cp2);
  823.       m->to = strdup(newto);
  824.    }
  825.  
  826.    if(msgid != NULLCHAR || date != NULLCHAR) {
  827.       *rhdr = mallocw(LINELEN);
  828.       sprintf(*rhdr,"In-Reply-To: your message ");
  829.       if(date != NULLCHAR) {
  830.          sprintf(m->line,"of %s.\n",date);
  831.          strcat(*rhdr,m->line);
  832.          if(msgid != NULLCHAR)
  833.             strcat(*rhdr,"             ");
  834.       }
  835.       if(msgid != NULLCHAR) {
  836.          sprintf(m->line,"%s\n",msgid);
  837.          strcat(*rhdr,m->line);
  838.       }
  839.       free(msgid);
  840.       free(date);
  841.    }
  842.    strcpy(m->line,subject);
  843.    return 0;
  844. }
  845.  
  846. void scanmail(m)       /* Get any new mail */
  847. struct mbx *m;
  848. {
  849.    FILE *nfile;
  850.    int ret, cnt;
  851.    char buf[256];
  852.    long diff;
  853.  
  854.    if ((diff = isnewmail(m)) == 0L)
  855.       return;
  856.    if(lockit(m))
  857.       return;
  858.    if(m->mfile == NULLFILE || diff < 0L) {
  859.       /* This is the first time scanmail is called, or the
  860.        * mail file size has decreased. In the latter case,
  861.        * any changes we did to this area will be lost, but this
  862.        * is not fatal.
  863.        */
  864.       initnotes(m);
  865.       rmlock(Mailspool,m->area);
  866.       return;
  867.    }
  868.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  869.    if ((nfile = fopen(buf,READ_TEXT)) == NULLFILE)
  870.       tprintf(Noaccess,buf);
  871.    else {
  872.       /* rewind tempfile */
  873.       fseek(m->mfile,0L,0);
  874.       cnt = m->nmsgs;
  875.       /* Reread all messages since size they may have changed
  876.        * in size after a X-Forwarded-To line was added. */
  877.       m->nmsgs = 0;
  878.       ret = readnotes(m,nfile,1);   /* get the mail */
  879.       m->newmsgs += m->nmsgs - cnt;
  880.       m->mboxsize = ftell(nfile);
  881.       if(!stricmp(m->name,m->area))
  882.          m->mysize = m->mboxsize;
  883.       (void) fclose(nfile);
  884.       if (ret != 0)
  885.          tprintf("Error updating mail file\n");
  886.    }
  887.    rmlock(Mailspool,m->area);
  888. }
  889.  
  890. /* Check the current mailbox to see if new mail has arrived.
  891.  * Returns the difference in size.  */
  892. static long isnewmail(m)
  893. struct mbx *m;
  894. {
  895.    char buf[256];
  896.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  897.    return fsize(buf) - m->mboxsize;
  898. }
  899.  
  900. /* Check if the private mail area has changed */
  901. long isnewprivmail(m)
  902. struct mbx *m;
  903. {
  904.    long cnt;
  905.    char buf[256];
  906.    sprintf(buf,"%s/%s.txt",Mailspool,m->name);
  907.    cnt = m->mysize;
  908.    m->mysize = fsize(buf);
  909.    return m->mysize - cnt; /* != 0 not more than once */
  910. }
  911.  
  912. char *Hdrs[] = {
  913.    "Approved: ",
  914.    "From: ",
  915.    "To: ",
  916.    "Date: ",
  917.    "Message-Id: ",
  918.    "Subject: ",
  919.    "Received: ",
  920.    "Sender: ",
  921.    "Reply-To: ",
  922.    "Status: ",
  923.    "X-BBS-Msg-Type: ",
  924.    "X-Forwarded-To: ",
  925.    "Cc: ",
  926.    "Comment: ",
  927.    "Organisation: ",
  928.    "R:",
  929.    NULLCHAR
  930. };
  931.  
  932. /* return the header type */
  933. int htype(s)
  934. char *s;
  935. {
  936.    register char *p;
  937.    register int i;
  938.  
  939.    p = s;
  940.    /* check to see if there is a ':' before and white space */
  941.    while (*p != '\0' && *p != ' ' && *p != ':')
  942.       p++;
  943.    if (*p != ':')
  944.       return NOHEADER;
  945.  
  946.    for (i = 0; Hdrs[i] != NULLCHAR; i++) {
  947.       if (strnicmp(Hdrs[i],s,strlen(Hdrs[i])) == 0)
  948.          return i;
  949.    }
  950.    return UNKNOWN;
  951. }
  952.  
  953. /* This function returns the length of a file. The proper thing would be
  954.  * to use stat(), but it fails when using DesqView together with Turbo-C
  955.  * code.  */
  956. static long fsize(name)
  957. char *name;
  958. {
  959.    long cnt;
  960.    FILE *fp;
  961.    if((fp = fopen(name,READ_TEXT)) == NULLFILE)
  962.       return -1L;
  963.    fseek(fp,0L,2);
  964.    cnt = ftell(fp);
  965.    fclose(fp);
  966.    return cnt;
  967. }
  968.  
  969. /* close the temporary mail file */
  970. static void mfclose(m)
  971. struct mbx *m;
  972. {
  973.    if(m->mfile != NULLFILE)
  974.       fclose(m->mfile);
  975.    m->mfile = NULLFILE;
  976. #ifdef SETVBUF
  977.    free(stdoutbuf);
  978.    stdoutbuf = NULLCHAR;
  979. #endif
  980. }
  981.  
  982. /* Parse a string in the "Text: <user@host>" or "Text: user@host (Text)"
  983.  * format for the address user@host.
  984.  */
  985. char *getaddress(string,cont)
  986. char *string;
  987. int cont;      /* true if string is a continued header line */
  988. {
  989.    char *cp, *ap = NULLCHAR;
  990.    int par = 0;
  991.    if((cp = getname(string)) != NULLCHAR) /* Look for <> style address */
  992.       return cp;
  993.    cp = string;
  994.    if(!cont)
  995.       if((cp = strchr(string,':')) == NULLCHAR)   /* Skip the token */
  996.          return NULLCHAR;
  997.       else
  998.          ++cp;
  999.    for(; *cp != '\0'; ++cp) {
  1000.       if(par && *cp == ')') {
  1001.          --par;
  1002.          continue;
  1003.       }
  1004.       if(*cp == '(')      /* Ignore text within parenthesis */
  1005.          ++par;
  1006.       if(par)
  1007.          continue;
  1008.       if(*cp == ' ' || *cp == '\t' || *cp == ',') {
  1009.          if(ap != NULLCHAR)
  1010.             break;
  1011.          continue;
  1012.       }
  1013.       if(ap == NULLCHAR)
  1014.          ap = cp;
  1015.    }
  1016.    *cp = '\0';
  1017.    return ap;
  1018. }
  1019.